package xyz.tanxiao.links.common.utils;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.IDN;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;

/**
 * 网络工具类
 *
 * @author 万成波
 * @email 2300064869@qq.com
 * @createTime 2023-06-29 18:19:37 星期四
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class NetworkUtils {

	/**
	 * 国外开放的免费ip地址查询接口
	 */
	private static final String OPEN_IP_API = "http://ip-api.com/json/";

	/**
	 * 淘宝开放的免费ip地址查询接口
	 */
	private static final String TAOBAO_OPEN_IP_API = "https://ip.taobao.com/outGetIpInfo?ip=";


	/**
	 * 获取 HttpServletRequest
	 */
	public static HttpServletRequest getHttpServletRequest() {
		ServletRequestAttributes servletRequest = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		// 将 ServletRequestAttributes 对象设置为子线程共享
		RequestContextHolder.setRequestAttributes(servletRequest, true);
		if (Objects.nonNull(servletRequest)) {
			return servletRequest.getRequest();
		}
		return null;
	}


	/**
	 * 获取当前请求的ip地址
	 *
	 * @return ip地址
	 */
	public static String getRequestIp() {
		HttpServletRequest request = getHttpServletRequest();
		if (Objects.nonNull(request)) {
			return getRequestIp(request);
		}
		return null;
	}


	/**
	 * 获取当前请求的ip地址
	 *
	 * @param request HttpServletRequest
	 * @return ip地址
	 */
	public static String getRequestIp(HttpServletRequest request) {
		if (Objects.isNull(request)) {
			return null;
		}

		String ip = request.getHeader("x-forwarded-for");
		final String unknown = "unknown";
		final boolean exists = Objects.isNull(ip);

		// 解析代理
		if (exists || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (exists || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (exists || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (exists || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (exists || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
			if ("127.0.0.1".equals (ip) || "localhost".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
				// 获取本机IP地址
				InetAddress inetAddress = null;
				try {
					inetAddress = InetAddress.getLocalHost();
				} catch (Exception e) {
					e.printStackTrace ();
				}
				if (inetAddress != null) {
					ip = inetAddress.getHostAddress();
				}
			}
		}

		// 多个代理
		if (Objects.nonNull(ip) && ip.length () > 15) {
			int index = ip.indexOf(",");
			if (index > 0) {
				ip = ip.substring (0, index);
			}
		}
		return ip;
	}


	/**
	 * 获取本地ip地址列表
	 *
	 * @return 本地ip地址
	 */
	public static List<String> getLocalIPItems() {
		List<String> ips = new ArrayList<>(1);
		try {
			// 获取本机的所有网络接口
			Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
			while (interfaces.hasMoreElements()) {
				NetworkInterface ni = interfaces.nextElement();
				// 过滤 loopback设备、虚拟网卡
				if (ni.isLoopback() || ni.isVirtual()) {
					continue;
				}
				// 获取网卡的IP地址
				Enumeration<InetAddress> addresses = ni.getInetAddresses();
				while (addresses.hasMoreElements()) {
					InetAddress address = addresses.nextElement();
					if (address.isSiteLocalAddress()) {
						ips.add(address.getHostAddress());
					}
				}
			}
		} catch (SocketException e) {
			e.printStackTrace();
		}
		return ips;
	}


	/**
	 * 获取ip地址的地理位置
	 *
	 * @param ip ip地址
	 * @return 地理位置
	 */
	public static String getIpLocation(String ip) {
		String ipLocation = getIpLocationByIpApi(ip);
		if (Objects.isNull(ipLocation)) {
			ipLocation = getIpLocationByTaobao(ip);
		}
		return ipLocation;
	}


	/**
	 * 获取ip地址的地理位置 (IpApi)
	 *
	 * @param ip ip地址
	 * @return 地理位置
	 */
	public static String getIpLocationByTaobao(String ip) {
		try {
			// 查询ip地址地理位置信息
			HttpRequest request = HttpUtil.createGet(TAOBAO_OPEN_IP_API + ip + "&accessKey=alibaba-inc");
			request.setConnectionTimeout(3000);
			HttpResponse response = request.execute();
			// 解析国家、省份、城市信息 (行政区划信息)
			JSONObject ipJson = JSONUtil.parseObj(response.body());
			if (ipJson.getInt("code") == 0) {
				JSONObject ipData = ipJson.getJSONObject("data");
				String country = ipData.getStr("country");
				String regionName = ipData.getStr("region");
				String city = ipData.getStr("city");
				return country + " " + regionName + " " + city;
			}
		} catch (Exception e) {
			log.error("=> 调用 {} 出现异常：{}", TAOBAO_OPEN_IP_API, e.getMessage());
		}
		return null;
	}


	/**
	 * 获取ip地址的地理位置 (淘宝)
	 *
	 * @param ip ip地址
	 * @return 地理位置
	 */
	public static String getIpLocationByIpApi(String ip) {
		try {
			// 查询ip地址地理位置信息
			HttpRequest request = HttpUtil.createGet(OPEN_IP_API + ip + "?lang=zh-CN");
			request.setConnectionTimeout(3000);
			HttpResponse response = request.execute();
			// 解析国家、省份、城市信息 (行政区划信息)
			JSONObject ipJson = JSONUtil.parseObj(response.body());
			if ("success".equals(ipJson.getStr("status"))) {
				String country = ipJson.getStr("country");
				String regionName = ipJson.getStr("regionName");
				String city = ipJson.getStr("city");
				return country + " " + regionName + " " + city;
			}
		} catch (Exception e) {
			log.error("=> 调用 {} 出现异常：{}", OPEN_IP_API, e.getMessage());
		}
		return null;
	}


	/**
	 * 将中文域名转换为ASCII字符集 (不包括协议部分)
	 *
	 * @param domainName 中文域名
	 * @return ASCII字符集域名
	 */
	public static String chineseDomainNameToASCII(String domainName) {
		String split = "://";
		String[] hosts = domainName.split(split);
		return hosts[0] + split + IDN.toASCII(hosts[1]);
	}

}
